﻿using System;
using System.Data;

using BizElements.Core.ProviderComponents;

namespace BizElements.Core
{
    /// <summary>
    /// Common interface for all data-access objects. 
    /// Specifies that compatibile data-access objects must be able to share connection and transaction context.
    /// </summary>
    public interface IDataAccessObject
    {
        #region Properties.

        /// <summary>
        /// Connection provider used for database interaction.
        /// </summary>
        IConnectionProvider ConnectionProvider { get; set; }

        /// <summary>
        /// Gets the information for the last command generated and executed by the current DAO on the data-source. <b>Nullable</b>.
        /// </summary>
        /// <remarks>Use only for debuging and tracing purposses to examine the SQL command that was actually 
        /// executed on the data-source. In some situations DAOs may not set or update the statistics.</remarks>
        CommandExecutionStatistics LastExecutedCommandInfo { get; }

        #endregion
    }

    /// <summary>
    /// Base class for all data-access objects. Inherited DAO classes may share connection and transaction objects.
    /// </summary>
    [Serializable]
    public class DaoBase : IDataAccessObject, IDisposable
    {
        // Class is marked as serializable because that is required by EntityBase class.
        // Note: DaoBase members are not serialized when EntityBase is serialized, 
        // however it needs to be marked as serializable; otherwise .NET serialization engine would throw exception.

        #region Members.

        /// <summary>
        /// Indicates whether the connection is openned locally or is it shared with other objects.
        /// If true then Dispose method will dispose ConnectionProvider, otherwise it will leave it intact.
        /// </summary>
        [NonSerialized]
        bool connectionIsCreatedLocal = true;

        /// <summary>
        /// ConnectionProvider object used for database interaction.
        /// </summary>
        [NonSerialized]
        IConnectionProvider connectionProvider;        

        /// <summary>
        /// Information for the last command generated and executed by the current DAO on the data-source.
        /// </summary>
        [NonSerialized]
        CommandExecutionStatistics lastExecutedCommandInfo;

        /// <summary>Catalog that creates connection providers on 1st demand.</summary>
        [NonSerialized]
        ICatalog dbCatalog;

        #endregion

        #region Constructors.

        /// <summary>
        /// Initializes a new instance of the DaoBase class.
        /// </summary>
        /// <param name="dbCatalog">Database catalog to which the connection will be provided by this instance.</param>
        public DaoBase(ICatalog dbCatalog)
        {
            this.connectionIsCreatedLocal = true;
            this.dbCatalog = dbCatalog;
        }

        /// <summary>
        /// Initializes a new instance of the DaoBase class. 
        /// </summary>
        /// <param name="conn">Connection provider the DAO will use to perform data-access operations. Null is not allowed.</param>
        public DaoBase(IConnectionProvider conn)
        {
            this.connectionIsCreatedLocal = false;
            this.connectionProvider = conn;
        }

        #endregion

        #region Dispose.

        /// <summary>
        /// Indicates whether the object has been disposed.
        /// </summary>
        [NonSerialized]
        private bool isDisposed;

        /// <summary>
        /// Releases the resources used by this object.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Releases the resources used by this object.
        /// </summary>
        protected virtual void Dispose(bool isDisposing)
        {
            // Check to see if Dispose has already been called.
            if (!this.isDisposed)
            {
                if (isDisposing)
                {
                    // Dispose managed resources.
                    if (this.connectionIsCreatedLocal && this.connectionProvider != null)
                    {
                        // Object is created in this class, so destroy it here.
                        this.connectionProvider.Dispose();
                    }

                    // Remove reference.
                    this.connectionProvider = null;
                    this.connectionIsCreatedLocal = false;
                }
            }

            this.isDisposed = true;
        }

        #endregion

        private void TryEnsureConnectionProvider()
        {
            if (this.connectionProvider == null && this.dbCatalog != null)
            {
                lock (this.dbCatalog)
                {
                    this.connectionProvider = this.dbCatalog.CreateConnectionProvider();
                }
            }
        }

        #region Properties.

        /// <summary>
        /// Gets or sets the ConnectionProvider used for database interaction. Nulls are not allowed.
        /// </summary>
        public IConnectionProvider ConnectionProvider
        {
            get
            {
                TryEnsureConnectionProvider();
                return this.connectionProvider;
            }
            set
            {
                // Null is not allowed.
                if (value == null)
                    throw new ArgumentNullException("value", Messages.DaoBase_ConnectionProviderMayNotBeNull);

                // Dispose old provider if allowed.
                if (this.connectionProvider != null)
                {
                    if (this.connectionIsCreatedLocal)
                        this.connectionProvider.Dispose();

                    this.connectionProvider = null;
                }

                // Assign new provider.
                this.connectionProvider = value;
                this.connectionIsCreatedLocal = false;
            }
        }

        /// <summary>
        /// Gets the information for the last command generated and executed by the current DAO on the data-source. <b>Nullable.</b>
        /// </summary>
        /// <remarks>Use only for debuging and tracing purposses to examine the SQL command that was actually 
        /// executed on the data-source. In some situations DAOs may not set or update the statistics.</remarks>
        public CommandExecutionStatistics LastExecutedCommandInfo
        {
            get { return this.lastExecutedCommandInfo; }
            protected set { this.lastExecutedCommandInfo = value; }
        }

        #endregion
    }
}